#include "StdAfx.h"
#include "Connector.h"
#include "TableConfig.h"
#include "MICEXTable.h"
#include <boost\thread.hpp>

Connector::Connector(void) 
	: connected(false)
	, dataSchema(new DataSchema())
	, stopped(true)
{
}


void Connector::ProcessingCycle()
{
	while (true)
	{
		CheckConnection();

		ProcessTables();

		if (stopped)
		{
			Disconnect();
			return;
		}
	}
}

void Connector::CheckConnection()
{
	if (!connected)
	{
		Connect();
	}
}

void Connector::Start()
{
	Stop();
	stopped = false;
	thread = std::shared_ptr<boost::thread>(new boost::thread(boost::bind(&Connector::ProcessingCycle, this)));
}

void Connector::Stop()
{
	if (thread != 0)
	{
		stopped = true;
		thread->join();
		thread.reset();
	}
}

bool Connector::Connect()
{
	char ErrorMsg[255];

	idx = MTEConnect(const_cast<char*>(sectionConfig->ConnectionString.c_str()), ErrorMsg);
	
	if( idx < MTE_OK ) 
	{
		connected = false;
		return false;
	}


	if (!ParseSchema() || !OpenTables())
	{
		Disconnect();
		return false;
	}

	connected = true;
	return true;
}

bool Connector::ParseSchema()
{
	if(idx >= MTE_OK) 
	{
		TMTEMsg* Msg;
		int32 Err;

		if((Err = MTEStructure(idx, &Msg)) != MTE_OK) 
		{
			if( Err == MTE_TSMR )
			{
 				char* pData = (char *)(Msg + 1);
				//LOG("Structure error: %s", pData);
			}
			else
			{
				char* errorMsg = MTEErrorMsg(Err);
				//LOG("Structure error: %s", MTEErrorMsg(Err));
			}
			return false;
		}
		else 
		{
			//LOG("Information objects description received");
			dataSchema->ParseMICEXSchema((char *)(Msg + 1), Msg->DataLen);
			//LOG("Information objects description parsed");
		}
	}
	return true;
}

bool Connector::OpenTables()
{
	for(VEC_CONFIG_TABLES::const_iterator it = sectionConfig->Tables.begin(); it != sectionConfig->Tables.end(); ++it)
	{
		TMTEMsg* Msg;
		char* pData;
		char* pTempPointer;

		//LOG_INFO(SECTION(lSystem), "%s: opening %s table...", (LPCSTR)m_pConfig->m_csName, (LPCSTR)pConfigMicexTable->m_csTableName);
		MICEXTablePtr pTable = dataSchema->GetTable(it->get()->TableName);
		if (pTable != 0)
		{
			try
			{
				pTable->TableHandle = MTE_NOTCONNECTED;
				pTable->TableHandle = MTEOpenTable(idx, (LPSTR)it->get()->TableName.c_str(), (LPSTR)it->get()->TableParams.c_str(), it->get()->CompleteFlag, &Msg);
			}
			catch(...)
			{
			}

			if(pTable->TableHandle >= MTE_OK)
			{
				pData = (char *)(Msg + 1);
				pTempPointer = pData;

				dataSchema->MICEXParseTableData(&pTempPointer, pTable);
			
				//LOG_INFO(SECTION(lSystem), "%s: opening %s completed.", (LPCSTR)m_pConfig->m_csName, (LPCSTR)pConfigMicexTable->m_csTableName);
			}
			else 
			{
				//LOG_ERROR(SECTION(lSystem), "%s: unable to open %s table.", (LPCSTR)m_pConfig->m_csName, (LPCSTR)pConfigMicexTable->m_csTableName);
				return false;
			}
		}
		else
		{
			//LOG("Unable to find %s table", it->get()->TableName);
			return false;
		}
	}

	return true;
}

void Connector::Disconnect()
{
	if (idx < MTE_OK) 
		return;
	
	//LOG_INFO(SECTION(lSystem), "Attempt to disconnect %s section...", (LPCSTR)m_pConfig->m_csName);

	CloseTables();

	//LOG_INFO(SECTION(lSystem), "%s: Disconnecting...", m_pConfig->m_csName);

	int32 err;
	if ((err = MTEDisconnect(idx)) == MTE_OK )
	{
		//LOG_INFO(SECTION(lSystem), "%s  section disconnected.", (LPCSTR)m_pConfig->m_csName);
	}
	else
	{
		//LOG_ERROR(SECTION(lSystem), "%s  section disconnect error: %s", (LPCSTR)m_pConfig->m_csName, MTEErrorMsg(Err));
	}

	connected = false;
}

void Connector::CloseTables()
{
	//TODO: incapsulte get all tables...
	const VEC_MICEX_TABLES& tables = dataSchema->GetAllTables();
	for(VEC_MICEX_TABLES::const_iterator it = tables.begin(); it != tables.end(); ++it)
	{
		if((*it)->TableHandle >= MTE_OK)
		{
			MTECloseTable(idx, (*it)->TableHandle);
			(*it)->TableHandle = MTE_TEUNAVAIL;
		}
	}
}

bool Connector::ProcessTables()
{
	TMTEMsg* Msg;
	int32 Err;

	if(idx >= MTE_OK) 
	{
		for (VEC_CONFIG_TABLES::iterator it = sectionConfig->Tables.begin(); it != sectionConfig->Tables.end(); ++it)
		{
			TableConfigPtr pConfigMicexTable = *it;
            
			MICEXTablePtr pTable = dataSchema->GetTable(pConfigMicexTable->TableName);

			if(pTable->TableHandle >= MTE_OK)
				MTEAddTable(idx, pTable->TableHandle, pTable->TableRef);
		}

		if((Err = MTERefresh(idx, &Msg)) == MTE_OK) 
		{
			char* pData = (char *)(Msg + 1);

			if(pData != NULL)
			{
				char* pTempPointer = pData;
				int32 nTables = *((int32*)pTempPointer);
				pTempPointer += sizeof(int32);

				for(int32 nTableCnt = 0; nTableCnt < nTables; nTableCnt++) 
				{
					int32 nRefTable = *((int32*)pTempPointer);
					MICEXTablePtr pTable = dataSchema->GetTable(nRefTable);

					dataSchema->MICEXParseTableData(&pTempPointer, pTable);
				}
			}
		}
		else
		{
			//LOG_ERROR(SECTION(lSystem), "%s: unable to refresh tables.", (LPCSTR)m_pConfig->m_csName);
			return ReConnect();
		}
	}

	return true;
}

bool Connector::ReConnect()
{
	Disconnect();
	return Connect();
}

Connector::~Connector(void)
{
}
